xfs: fix log recovery buffer allocation for the legacy h_size fixup [ Upstream commit 45cf976008ddef4a9c9a30310c9b4fb2a9a6602a ] Note: The upstream commit was adjusted to use kmem_free instead of kvfree since kmem_free was used in xfs_log_recover.c until commit 49292576136f (xfs: convert kmem_free() for kvmalloc users to kvfree()), and the remainder of the file still uses kmem_free. Commit a70f9fe52daa ("xfs: detect and handle invalid iclog size set by mkfs") added a fixup for incorrect h_size values used for the initial umount record in old xfsprogs versions. Later commit 0c771b99d6c9 ("xfs: clean up calculation of LR header blocks") cleaned up the log reover buffer calculation, but stoped using the fixed up h_size value to size the log recovery buffer, which can lead to an out of bounds access when the incorrect h_size does not come from the old mkfs tool, but a fuzzer. Fix this by open coding xlog_logrec_hblks and taking the fixed h_size into account for this calculation. BUG=b/352003129 TEST=presubmit, xfstests on stable kernel RELEASE_NOTE=Fixed CVE-2024-39472 in the linux kernel cos-patch: security-moderate Fixes: 0c771b99d6c9 ("xfs: clean up calculation of LR header blocks") Reported-by: Sam Sun <samsun1006219@gmail.com> Change-Id: I1fc6411763005d2d199f98ed78bb64983fb0e99a Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Brian Foster <bfoster@redhat.com> Reviewed-by: "Darrick J. Wong" <djwong@kernel.org> Signed-off-by: Chandan Babu R <chandanbabu@kernel.org> Reviewed-on: https://cos-review.googlesource.com/c/third_party/kernel/+/78480 Reviewed-by: Oleksandr Tymoshenko <ovt@google.com> Main-Branch-Verified: Cusky Presubmit Bot <presubmit@cos-infra-prod.iam.gserviceaccount.com> Tested-by: Cusky Presubmit Bot <presubmit@cos-infra-prod.iam.gserviceaccount.com> 
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 3d844a2..705cd5a 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c 
@@ -2972,7 +2972,7 @@ xlog_do_recovery_pass( 	int	error = 0, h_size, h_len; 	int	error2 = 0; 	int	bblks, split_bblks; -	int	hblks, split_hblks, wrapped_hblks; +	int	hblks = 1, split_hblks, wrapped_hblks; 	int	i; 	struct hlist_head	rhash[XLOG_RHASH_SIZE]; 	LIST_HEAD	(buffer_list); @@ -3028,14 +3028,22 @@ xlog_do_recovery_pass( 	if (error) 	goto bread_err1;   -	hblks = xlog_logrec_hblks(log, rhead); -	if (hblks != 1) { -	kmem_free(hbp); -	hbp = xlog_alloc_buffer(log, hblks); +	/* + * This open codes xlog_logrec_hblks so that we can reuse the + * fixed up h_size value calculated above. Without that we'd + * still allocate the buffer based on the incorrect on-disk + * size. + */ +	if (h_size > XLOG_HEADER_CYCLE_SIZE && + (rhead->h_version & cpu_to_be32(XLOG_VERSION_2))) { +	hblks = DIV_ROUND_UP(h_size, XLOG_HEADER_CYCLE_SIZE); +	if (hblks > 1) { +	kmem_free(hbp); +	hbp = xlog_alloc_buffer(log, hblks); +	} 	} 	} else { 	ASSERT(log->l_sectBBsize == 1); -	hblks = 1; 	hbp = xlog_alloc_buffer(log, 1); 	h_size = XLOG_BIG_RECORD_BSIZE; 	}